//The MIT License
//
//Copyright (c) 2009 nodchip
//
//Permission is hereby granted, free of charge, to any person obtaining a copy
//of this software and associated documentation files (the "Software"), to deal
//in the Software without restriction, including without limitation the rights
//to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
//copies of the Software, and to permit persons to whom the Software is
//furnished to do so, subject to the following conditions:
//
//The above copyright notice and this permission notice shall be included in
//all copies or substantial portions of the Software.
//
//THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
//IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
//FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
//AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
//LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
//OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
//THE SOFTWARE.
package tv.dyndns.kishibe.server;

import java.awt.Canvas;
import java.awt.Image;
import java.awt.image.AreaAveragingScaleFilter;
import java.awt.image.BufferedImage;
import java.awt.image.FilteredImageSource;
import java.awt.image.ImageFilter;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import javax.imageio.ImageIO;
import javax.servlet.Servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import tv.dyndns.kishibe.client.constant.Constant;
import tv.dyndns.kishibe.client.packet.PacketUserData;
import tv.dyndns.kishibe.server.database.Database;
import tv.dyndns.kishibe.server.database.DatabaseException;

import com.google.inject.Inject;
import com.google.inject.Singleton;

@Singleton
public class ServiceIconUpload extends HttpServlet implements Servlet {
	private static final Logger logger = Logger.getLogger(ServiceIconUpload.class.getName());
	private static final long serialVersionUID = -7542423724548967280L;
	private final Database database;

	@Inject
	public ServiceIconUpload(Database database) {
		this.database = database;
	}

	protected void doPost(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		try (InputStream inputStream = request.getInputStream();
				OutputStream outputStream = response.getOutputStream()) {
			processRequest(request, response);
		}
	}

	private void processRequest(HttpServletRequest request, HttpServletResponse response)
			throws IOException, ServletException {
		FileItemFactory factory = new DiskFileItemFactory();
		ServletFileUpload upload = new ServletFileUpload(factory);
		upload.setSizeMax(64L * 1024L);
		List<FileItem> items = null;

		try {
			@SuppressWarnings("unchecked")
			List<FileItem> temp = upload.parseRequest(request);
			items = temp;
		} catch (FileUploadException e) {
			writeResponse(response, Constant.ICON_UPLOAD_RESPONSE_FAILED_TO_PARSE_REQUEST);
			return;
		}

		String userCode = null;
		BufferedImage inputImage = null;
		long originalFileSize = 0;

		for (FileItem item : items) {
			if (item.isFormField()) {
				String key = item.getFieldName();
				String value = item.getString();

				if (key.equals(Constant.FORM_NAME_USER_CODE)) {
					userCode = value;
				}

				continue;
			}

			// 画像確認
			originalFileSize = item.getSize();
			try (InputStream inputStream = item.getInputStream()) {
				inputImage = ImageIO.read(inputStream);
			} catch (IOException e) {
				writeResponse(response,
						Constant.ICON_UPLOAD_RESPONSE_FAILED_TO_DETECT_IMAGE_FILE_TYPE);
				return;
			}

			if (inputImage == null) {
				writeResponse(response,
						Constant.ICON_UPLOAD_RESPONSE_FAILED_TO_DETECT_IMAGE_FILE_TYPE);
				return;
			}
		}

		if (userCode == null || inputImage == null) {
			writeResponse(response, Constant.ICON_UPLOAD_RESPONSE_REQUEST_FORMAT_ERROR);
			return;
		}

		String extension = "jpg";
		String fileTitle = Utility.createFileName();
		String fileName = fileTitle + "." + extension;

		// リサイズ
		// TODO(nodchip): ユーティリティクラスへ抽出
		int size = Constant.ICON_SIZE * 2;
		ImageFilter imageFilter = new AreaAveragingScaleFilter(size, size);
		Image middleImage = new Canvas().createImage(new FilteredImageSource(
				inputImage.getSource(), imageFilter));
		BufferedImage outputImage = new BufferedImage(size, size, BufferedImage.TYPE_INT_RGB);
		outputImage.createGraphics().drawImage(middleImage, 0, 0, size, size, null);

		// ファイル書き込み
		new File(Constant.ICON_FOLDER_PATH).mkdirs();
		File outputFile = new File(Constant.ICON_FOLDER_PATH, fileName);
		ImageIO.write(outputImage, "jpg", outputFile);

		logger.log(Level.INFO, String.format("%d bytes -> %d bytes (%s)", originalFileSize,
				outputFile.length(), outputFile.getPath()));

		// データベース書き込み
		int userId = Integer.parseInt(userCode);
		PacketUserData userData;
		try {
			userData = database.getUserData(userId);
		} catch (DatabaseException e) {
			logger.log(Level.WARNING, "ユーザーデータの読み込みに失敗しました", e);
			throw new ServletException(e);
		}
		File oldFile = new File(Constant.ICON_FOLDER_PATH, userData.imageFileName);
		if (oldFile.isFile() && !oldFile.getName().equals(Constant.ICON_NO_IMAGE)) {
			oldFile.delete();
		}
		userData.imageFileName = fileName;
		try {
			database.setUserData(userData);
		} catch (DatabaseException e) {
			logger.log(Level.WARNING, "ユーザーデータの保存に失敗しました", e);
			throw new ServletException(e);
		}

		// レスポンス書き込み
		writeResponse(response, Constant.ICON_UPLOAD_RESPONSE_OK);
	}

	private void writeResponse(HttpServletResponse response, String message) throws IOException {
		response.setContentType("text/plain");
		response.getOutputStream().write(message.getBytes());
	}
}
